// phasespider.txt
// Combat AI and attack abilities for the Phase Spider

// Walker M. White 
// Revision 1.2, 
// March 30, 2004
// Revision history:
// 1.2: Various bug fixes from learning more AvernumScript ideosyncracies.
// 1.1: Revised to include REAL teleportation using relocate_character.
// This is much more balanced as it uses the built-in system for attack and 
// damage instead of my simulated attacks.

// Permission is granted for free use of this script in scenarios, provided that
// these header comments are retained and modifications are dutifully documented
// throughout.

// This creature is designed as a L9 creature, and damage generated in this
// script is within the spirit of the regular damage rules.
// The suggested template for this creature is as follows:
// 	begindefinecreature CREATURE_NUMER;
//		clear;
//		cr_name = "Phase Spider";
//		cr_level = 9;
//		cr_hp_bonus = 5;
//		cr_default_attitude = 4;
//		cr_default_script = "phasespider";
//		cr_species = 9;
//		cr_which_sheet = 1556;
//		cr_attack_1 = 6;
//		cr_summon_class = 5;
//		cr_special_abil = 24;

// This script extends basicnpc.txt, except that Phase Spider mobility has been
// altered, and thus the semantics of Cell 0 is different.  Phase Spiders do
// not move normally unless they are cornered and have nowhere to teleport.  
// Otherwise, they teleport between melee and several, predefined "safety" 
// locations.  The starting location is always a safety location, and the Phase
// Spider can have up to three more safety locations as defined by memory cells 
// 4-9.  Look at the Battle Arena example to see a correct use of Phase Spider 
// memory cells.
// Cell 0: Number of safety locations.  If 0, only the starting location is a
//         safety location.  If 1, an additional safety location is defined in
//         Cells 4,5.  If 2, then there are two locations, each in 4,5 and 6,7.
//         If a third point is stored cells 8,9, then this value should be 3.
//         Memory cells may be ignored if this value is not correct.  If this
//         value is out of bounds (not 0, 1, 2, or 3), then it is set to 0.
// Cell 1: Stuff done flag 1 (as per basicnpc)
// Cell 2: Stuff done flag 2 (as per basicnpc)
// Cell 3: Dialog node       (as per basicnpc)
// Cell 4: The x-coordinate of the first alternate safety location.
//         (other than the starting location)
// Cell 5: The y-coordinate of the first alternate safety location.
//         (other than the starting location)
// Cell 6: The x-coordinate of the second alternate safety location.
//         (other than the starting location)
// Cell 7: The y-coordinate of the second alternate safety location.
//         (other than the starting location)
// Cell 8: The x-coordinate of the third alternate safety location.
//         (other than the starting location)
// Cell 9: The y-coordinate of the third alternate safety location.
//         (other than the starting location)

begincreaturescript;

variables;

// Target acquisition variables.
short target;
short target_x,target_y;
short x, y;

// Web damage.
short dmg;

// Variables for teleportation
short start_x, start_y;
short jump_to_x, jump_to_y;

// Safety location management
short safety_locations;
short current_pos, next_pos;

// All purpose loop variables
short i, j;

body;

beginstate INIT_STATE;

	// Remember where we started
	start_x = my_loc_x();
	start_y = my_loc_y();

	// Check the number of safety locations is valid before loading up.
	safety_locations = get_memory_cell(0);
	if (safety_locations < -1 || safety_locations > 3) {
		safety_locations = 0;
	}
			
	// We start play in our starting position.
	current_pos = 0;
	
	break;

beginstate DEAD_STATE;
	// Set the appropriate stuff done flag for this character being dead
	if ((get_memory_cell(1) != 0) || (get_memory_cell(2) != 0))
		set_flag(get_memory_cell(1),get_memory_cell(2),1);
	break;

beginstate START_STATE; 
	// The start state assumes that we are in one of our "safety" locations.

	// If we are hit by a _living_ target, run away.
	if (who_hit_me() >= 0) {
		// Target may have died in that time.
	 	if (char_ok(who_hit_me()) == TRUE) {
			set_target(ME,who_hit_me());
			set_state_continue(6);
		}
	}
	
	// Otherwise, concentrate on current target
	if (target_ok()) {

		// Web anyone in range.
		if (dist_to_char(get_target()) <= 16) {
			set_state_continue(3);
		}
		
		// Do not hunt down targets.
		set_target(ME,-1);
	}
	
	// If no target, look for one and attack it if visible
	if (select_target(ME,8,0)) {
		set_state(3);
	}
		
	// No movement; we do not fidget.

	// if we're in combat and the above didn't give me anything to do, just stop
	// now. Otherwise, game will keep running script, and that eats up CPU time.
	if (am_i_doing_action() == FALSE)
		end_combat_turn();
	break;
	
beginstate 3; // Web attack state.  Always enter when we acquire a new target.

	// If the target is ok, web him and assess afterwards.
	if (target_ok()) {
	
		// Jump out if target is too close.
		if (dist_to_char(get_target()) <= 1) {
			set_state_continue(6);
		}		
	
		target_x = char_loc_x(get_target());
		target_y = char_loc_y(get_target());		
	
		// Warn the player that the Phase Spider is attacking.
		print_named_str(ME,"shoots forth a web.");
		// Bug: get_level does not work with ME.
		dmg = (get_level(my_number())/4)+1;  // This is a good web damage value.
		put_jagged_zap(my_loc_x(),my_loc_y(),target_x,target_y,4);
		put_object_on_space(target_x,target_y,0);
		run_animation_sound(64);
		set_char_status(target,6,dmg,0,0);
	
		// End combat and assess the target.
		end_combat_turn();
		
		// No more than a total of two web hits.
		dmg = 2*dmg;  
		if (get_char_status(target,6) >= dmg) {
			set_state(5); // Teleport into melee.
		}
	} else { // Find a new target.
		set_state(START_STATE);
	} 
	break;


beginstate 4; // Melee attack state.
	// If we are hit by a _living_ target, run away.
	if (who_hit_me() >= 0 && char_ok(who_hit_me()) == TRUE) {
		set_target(ME,who_hit_me());
		set_state_continue(6);
	}	

	// Otherwise attack the target.
	if (target_ok()) {
		do_attack();
	} else {
		// If target is dead, teleport to safety.  
		// This will select a new target.
		set_state_continue(6);
	}
	break;

beginstate 5; // Teleport into melee state.

	// Pick a random adjacent location.
	x = get_ran(1,0,2);
	y = get_ran(1,0,2);
	i = (x + 1) % 3;
	j = (y + 1) % 3;
	
	// Cycle until we find an exceptable one.
	while (i != x) {
		while (j != y) {
			jump_to_x = (target_x - 1)+i;
			jump_to_y = (target_y - 1)+j;
					
			// Check if we "can" go there.  
			// Have I forgotten anything?
			if (can_see_loc(jump_to_x,jump_to_y) == TRUE  && 
				is_blocked(jump_to_x, jump_to_y) == FALSE  && 
				char_on_loc(jump_to_x,jump_to_y) == -1 &&
				// Check for objects in the way
				// Oh Jeff, why couldn't you have done this as a bit-vector?
				is_object_on_space(jump_to_x,jump_to_y,1) == FALSE &&
				is_object_on_space(jump_to_x,jump_to_y,2) == FALSE &&
				is_object_on_space(jump_to_x,jump_to_y,4) == FALSE &&
				is_object_on_space(jump_to_x,jump_to_y,5) == FALSE &&
				is_object_on_space(jump_to_x,jump_to_y,6) == FALSE &&
				// No attacking from above.
				get_height(jump_to_x,jump_to_y) == get_height(target_x,target_y)) {			    

					// Focus on where we are.
					force_view_center(my_loc_x(),my_loc_y());				    
					put_sparkles_on_char(ME,0,2); 
					run_animation_sound(163);

					// Teleport to the new location and enter melee state
					// Bug: relocate_character does not work with ME.
					relocate_character(my_number(),jump_to_x,jump_to_y);
					force_view_center(target_x,target_y);
					put_sparkles_on_space(jump_to_x,jump_to_y,0,2);
					run_animation_sound(165);
					set_state_continue(4);
			}
			j = (j+1) % 3;
		}
		i = (i+1) % 3;
	}
	
	// Did not find one.  Switch targets.
	set_state(START_STATE);
	break;

beginstate 6; // Teleport to a new safety location.

	// Cycle through the other locations until we find a spot we can jump to.
	i = 0;
	while (i < safety_locations) {
		next_pos = (current_pos+i+1) % (safety_locations+1);
		if (next_pos > 0) {
			j = 2*next_pos+2;
			jump_to_x = get_memory_cell(j);
			j = j+1;
			jump_to_y = get_memory_cell(j);
		} else {
			jump_to_x = start_x;
			jump_to_y = start_y;
		}
		
		// Check if we can jump there.
		// Have I forgotten anything?
		if (can_see_loc(jump_to_x,jump_to_y) == TRUE  && 
			is_blocked(jump_to_x, jump_to_y) == FALSE  && 
			char_on_loc(jump_to_x,jump_to_y) == -1 &&
			// Check for objects in the way
			// Oh Jeff, why couldn't you have done this as a bit-vector?
			is_object_on_space(jump_to_x,jump_to_y,1) == FALSE &&
			is_object_on_space(jump_to_x,jump_to_y,2) == FALSE &&
			is_object_on_space(jump_to_x,jump_to_y,4) == FALSE &&
			is_object_on_space(jump_to_x,jump_to_y,5) == FALSE &&
			is_object_on_space(jump_to_x,jump_to_y,6) == FALSE) {
				// Update the location.
				current_pos = next_pos;
			
				// Focus on where we are.
				force_view_center(my_loc_x(),my_loc_y());				    
				put_sparkles_on_char(ME,0,2); 
				run_animation_sound(163);
						
				// Teleport to the new location and enter web attack state.
				// Bug: relocate_character does not work with ME.
				relocate_character(my_number(),jump_to_x,jump_to_y);
				force_view_center(target_x,target_y);
				put_sparkles_on_space(jump_to_x,jump_to_y,0,2);
				run_animation_sound(165);
				set_state_continue(3);
		}
		i = i+1;
	}
		
	// Did not find anyone.  Run away! (But attack if cornered).
	current_pos = -1;
	if (flee_char(ME,get_target(),5) == TRUE) {
		do_attack();
	} else {
		set_state(3);
	}
	break;		

beginstate TALKING_STATE;
	if (get_memory_cell(3) == 0) {
		print_str("Talking: It doesn't respond.");
		end();
		}
	begin_talk_mode(get_memory_cell(3));
	break;

